package com.impetus.project.skilltest.client.pages;

import java.sql.Timestamp;
import java.util.Date;
import java.util.List;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.datepicker.client.DatePicker;
import com.impetus.project.skilltest.client.stub.SecurityService;
import com.impetus.project.skilltest.client.stub.SecurityServiceAsync;
import com.impetus.project.skilltest.client.stub.TestService;
import com.impetus.project.skilltest.client.stub.TestServiceAsync;
import com.impetus.project.skilltest.common.Constants;
import com.impetus.project.skilltest.common.SkillTestException;
import com.impetus.project.skilltest.common.dataobject.Candidate;
import com.impetus.project.skilltest.common.dataobject.ScheduledTest;


public class TestEditPage 
{
	private Throwable caught;
	private ScheduledTest schTest = null;
	private static final TestServiceAsync testService = GWT.create(TestService.class);
	private static final SecurityServiceAsync securityService = GWT.create(SecurityService.class);
	
	private Date startTimeDt = new Date();
	private Date endTimeDt = new Date(System.currentTimeMillis()+Constants.DEFAULT_TEST_EXPIRATION_TIME_MSEC);
		
	public boolean loadPage(final PageData pageData)throws SkillTestException
	{
		try{
			final Label statusLabel = new Label("Please Define the Test Schedule");
			
			if(pageData.getTest() != null
					&& pageData.getTest().getTestid() != null){
				testService.getScheduledTestByTestId(pageData.getTest().getTestid(),
						new AsyncCallback<List<ScheduledTest>>() {
					public void onSuccess(List<ScheduledTest> result) {
						if(result == null || result.isEmpty() || result.get(0) == null){
							caught = new SkillTestException("No report data found for given test.");
						}
						else{
							schTest = result.get(0);
						}
					}
					public void onFailure(Throwable exp) {
						caught = exp;
					}});
			}
			
			Grid grid = new Grid(8,2);
			Label lbl = new Label("Start Time");
			grid.setWidget(0, 0, lbl);
			lbl = new Label("Expiration Time");
			grid.setWidget(1, 0, lbl);
			lbl = new Label("Test Duration (Minutes)");
			grid.setWidget(2, 0, lbl);
			lbl = new Label("Test Participants");
			grid.setWidget(3, 0, lbl);
			lbl = new Label("Question Type");
			grid.setWidget(4, 0, lbl);
			lbl = new Label("No. of Qestions");
			grid.setWidget(5, 0, lbl);
			lbl = new Label("Complexity Level");
			grid.setWidget(6, 0, lbl);
			lbl = new Label("Technology");
			grid.setWidget(7, 0, lbl);
			
			final Label startTimeText = new Label(startTimeDt.toString());
			final Label endTimeText = new Label(endTimeDt.toString());
			DatePicker startTime = new DatePicker();
			DatePicker endTime = new DatePicker();
			startTime.addValueChangeHandler(new ValueChangeHandler<Date>() {
				public void onValueChange(ValueChangeEvent<Date> event) {
					startTimeDt = event.getValue();
					String dateString = DateTimeFormat.getMediumDateFormat().format(startTimeDt);
					startTimeText.setText(dateString);
				}
			});
			endTime.addValueChangeHandler(new ValueChangeHandler<Date>() {
				public void onValueChange(ValueChangeEvent<Date> event) {
					endTimeDt = event.getValue();
					String dateString = DateTimeFormat.getMediumDateFormat().format(endTimeDt);
					endTimeText.setText(dateString);
				}
			});
			HorizontalPanel hpanel = new HorizontalPanel();
			hpanel.add(startTimeText);
			hpanel.add(startTime);
			grid.setWidget(0, 1, hpanel);
			hpanel = new HorizontalPanel();
			hpanel.add(endTimeText);
			hpanel.add(endTime);
			grid.setWidget(1, 1, hpanel);
			
			final TextBox nDurationTB = new TextBox();
			nDurationTB.addKeyUpHandler(new NumericKeyHandler(nDurationTB));
			grid.setWidget(2, 1, nDurationTB);
			
			// Test Participants multiple-select box
			final ListBox candidateBox = new ListBox(true);
			candidateBox.setSize("20em", "10em");
			final DialogBox addCandidateDialogBox = createDialogBox(pageData, candidateBox);
			addCandidateDialogBox.setGlassEnabled(true);
			addCandidateDialogBox.setAnimationEnabled(true);
			Button createCandidateBut = new Button("Create New", new ClickHandler() {
				public void onClick(ClickEvent event) {
					addCandidateDialogBox.center();
					addCandidateDialogBox.show();
				}
			});
			// TODO: implement and integrate AJAX
			final TextBox candidateAjaxTB = new TextBox();
			Button addCandidateBut = new Button("Add", new ClickHandler() {
				public void onClick(ClickEvent event) {
					candidateBox.addItem(candidateAjaxTB.getText());
				}
			});
			hpanel = new HorizontalPanel();
			hpanel.add(candidateAjaxTB);
			hpanel.add(addCandidateBut);
			VerticalPanel tvp = new VerticalPanel();
			tvp.add(createCandidateBut);
			tvp.add(hpanel);
			hpanel = new HorizontalPanel();
			hpanel.add(tvp);
			hpanel.add(candidateBox);
			grid.setWidget(3, 1, hpanel);
			
			// Question type select drop down
			final ListBox qTypeDrop = new ListBox(false);
			qTypeDrop.addItem(Constants.QUESTION_TYPE.PROGRAMMING);
			qTypeDrop.addItem(Constants.QUESTION_TYPE.OBJECTIVE);
			grid.setWidget(4, 1, qTypeDrop);
			
			final TextBox nQuesTB = new TextBox();
			nQuesTB.addKeyUpHandler(new NumericKeyHandler(nQuesTB));
			grid.setWidget(5, 1, nQuesTB);
			
			// Question type select drop down
			final ListBox complexDrop = new ListBox(false);
			complexDrop.addItem(Constants.COMPLEXITY.ANY);
			complexDrop.addItem(Constants.COMPLEXITY.VERY_EASY);
			complexDrop.addItem(Constants.COMPLEXITY.EASY);
			complexDrop.addItem(Constants.COMPLEXITY.MEDIUM);
			complexDrop.addItem(Constants.COMPLEXITY.HARD);
			complexDrop.addItem(Constants.COMPLEXITY.VERY_HARD);
			grid.setWidget(6, 1, complexDrop);
			
			// Technology select drop down
			final ListBox technoDrop = new ListBox(false);
			technoDrop.addItem(Constants.QUESTION_DOMAIN.PROGRAMMING.JAVA);
			grid.setWidget(7, 1, technoDrop);
			
			// Populate the database values
			if(schTest != null){
				startTimeText.setText(schTest.getStartTime().toString());
				endTimeText.setText(schTest.getExpirationTime().toString());
				nDurationTB.setText(String.valueOf(schTest.getTestDurationMins()));
				candidateBox.addItem(schTest.getCandidateid());
				for(int i=0; i<qTypeDrop.getItemCount(); i++){
					if(qTypeDrop.getItemText(i).equalsIgnoreCase(schTest.getqType())){
						qTypeDrop.setItemSelected(i, true);
					}
				}
				nQuesTB.setText(String.valueOf(schTest.getQuestionCount()));
				for(int i=0; i<complexDrop.getItemCount(); i++){
					if(complexDrop.getItemText(i).equalsIgnoreCase(schTest.getComplexity())){
						complexDrop.setItemSelected(i, true);
					}
				}
				for(int i=0; i<technoDrop.getItemCount(); i++){
					if(technoDrop.getItemText(i).indexOf(schTest.getqDomains()) != -1){
						technoDrop.setItemSelected(i, true);
					}
				}
			}
			
			// Add the submit buttons...
			Button schBut = new Button("Schedule");
			Button cancelBut = new Button("Reset");
			hpanel = new HorizontalPanel();
			hpanel.add(schBut);
			hpanel.add(cancelBut);
			
			schBut.addClickHandler(new ClickHandler() {
				public void onClick(ClickEvent event) {
					try{
						statusLabel.setText("Save test execution started");
						ScheduledTest test = new ScheduledTest();
						if(schTest != null){
							test = schTest;
						}
						
						test.setComplexity(complexDrop.getItemText(complexDrop.getSelectedIndex()));
						test.setExpirationTime(new Timestamp(endTimeDt.getTime()));
						test.setIsEnabled(true);
						test.setqDomains(technoDrop.getItemText(technoDrop.getSelectedIndex()));
						test.setqType(qTypeDrop.getItemText(qTypeDrop.getSelectedIndex()));
						test.setQuestionCount(Integer.parseInt(nQuesTB.getText()));
						test.setStartTime(new Timestamp(endTimeDt.getTime()));
						test.setStatus(Constants.TEST_STATUS.SCHEDULED);
						test.setTestDurationMins(Integer.parseInt(nDurationTB.getText()));
						
						if(schTest == null){
							test.setCreatedby(pageData.getUser().getCandidateid());
							test.setCreatedon(new Timestamp(System.currentTimeMillis()));
						}
						test.setModifiedby(pageData.getUser().getCandidateid());
						test.setModifiedon(new Timestamp(System.currentTimeMillis()));
						
						caught = null;
						int loop = candidateBox.getItemCount();
						for(int i=0; i<loop; i++){
							test.setCandidateid(candidateBox.getItemText(i));
							final String strCount = (i+1)+" of "+loop;
							
							// add test to DB
							statusLabel.setText("Adding test to DB");
							testService.saveUpdateScheduledTest(test, new AsyncCallback<Void>(){
								public void onFailure(Throwable t) {
									caught = t;
									statusLabel.setText(caught.getMessage());
								}
								public void onSuccess(Void result) {
									statusLabel.setText("Saved Scheduled Test: "+strCount);
								}
							});
							
							if(caught != null){
								statusLabel.setText(caught.getMessage());
								break;
							}
						}					
					}catch(Exception e){
						statusLabel.setText(e.getMessage());
					}
					if(caught == null){
						PageRouter.loadPage(GUIConstants.PAGES.TEST_LISTING_PAGE, pageData);
					}
				}});
			
			HorizontalPanel menuPanel = TopMenu.drawMenu(pageData);
			VerticalPanel vpanel = new VerticalPanel();
			vpanel.addStyleName("center");
			vpanel.add(menuPanel);
			vpanel.add(statusLabel);
			vpanel.add(grid);
			vpanel.add(hpanel);
			
			/** TODO: remove this 'RootPanel.get().add(vpanel);' from everywhere
			 *  and return the vertical panel as widget from every loadPage method
			 *  further, render the vertical panel in the page router/ page loader.
			 */
			RootPanel.get().clear();
		    RootPanel.get().add(vpanel);
		    
			return true;
		}
		catch(Throwable ex){
			ex.printStackTrace();
			if(ex instanceof SkillTestException){
				throw (SkillTestException)ex;
			}else{
				throw new SkillTestException(ex);
			}
		}
	}
	
	private DialogBox createDialogBox(final PageData pageData, final ListBox candidateBox) 
	{
		final DialogBox dialogBox = new DialogBox();
		//dialogBox.ensureDebugId("cwDialogBox");
		dialogBox.setText("Add New Candidates to the system...");
		
		Grid grid = new Grid(11,2);
		final Label lLabel = new Label("Enter Details:");
		Label tLabel = new Label("User Id");
		grid.setWidget(0, 0, tLabel);
		tLabel = new Label("Password");
		grid.setWidget(1, 0, tLabel);
		tLabel = new Label("First Name");
		grid.setWidget(2, 0, tLabel);
		tLabel = new Label("Last Name");
		grid.setWidget(3, 0, tLabel);
		tLabel = new Label("Gender");
		grid.setWidget(4, 0, tLabel);
		tLabel = new Label("Date of birth (yyyy-mm-dd)");
		grid.setWidget(5, 0, tLabel);
		tLabel = new Label("Technology");
		grid.setWidget(6, 0, tLabel);
		tLabel = new Label("Qualification");
		grid.setWidget(7, 0, tLabel);
		tLabel = new Label("Experience In Months");
		grid.setWidget(8, 0, tLabel);
		tLabel = new Label("Contact No");
		grid.setWidget(9, 0, tLabel);
		tLabel = new Label("e-mail Address");
		grid.setWidget(10, 0, tLabel);
		
		final TextBox idTB = new TextBox();
		grid.setWidget(0, 1, idTB);
		final TextBox pswdTB = new TextBox();
		grid.setWidget(1, 1, pswdTB);
		final TextBox fnameTB = new TextBox();
		grid.setWidget(2, 1, fnameTB);
		final TextBox lnameTB = new TextBox();
		grid.setWidget(3, 1, lnameTB);
		final ListBox genderDrop = new ListBox(false);
		genderDrop.addItem("M");
		genderDrop.addItem("F");
		grid.setWidget(4, 1, genderDrop);
		final TextBox dobPicker = new TextBox();
		grid.setWidget(5, 1, dobPicker);
		final TextBox techTB = new TextBox();
		grid.setWidget(6, 1, techTB);
		final TextBox qualiTB = new TextBox();
		grid.setWidget(7, 1, qualiTB);
		final TextBox expTB = new TextBox();
		expTB.addKeyUpHandler(new NumericKeyHandler(expTB));
		grid.setWidget(8, 1, expTB);
		final TextBox phTB = new TextBox();
		phTB.addKeyUpHandler(new NumericKeyHandler(phTB));
		grid.setWidget(9, 1, phTB);
		final TextBox emailTB = new TextBox();
		grid.setWidget(10, 1, emailTB);
				
		HorizontalPanel butPanel = new HorizontalPanel();
		// Add an add button at the bottom of the dialog
		Button addButton = new Button("Add",
		    new ClickHandler() {
		      public void onClick(ClickEvent event) {
		    	  Candidate candidate = new Candidate();
		    	  candidate.setCandidateid(idTB.getText());
		    	  candidate.setPasswd(pswdTB.getText());
		    	  candidate.setFirstName(fnameTB.getText());
		    	  candidate.setLastName(lnameTB.getText());
		    	  candidate.setGender(genderDrop.getItemText(genderDrop.getSelectedIndex()));
		    	  candidate.setDob(java.sql.Date.valueOf(dobPicker.getText()));
		    	  candidate.setTechnology(techTB.getText());
		    	  candidate.setQualification(qualiTB.getText());
		    	  candidate.setExpInMonth(Integer.parseInt(expTB.getText()));
		    	  candidate.setContactNo(Integer.parseInt(phTB.getText()));
		    	  candidate.setEmail(emailTB.getText());
		    	  candidate.setIsEnabled(true);
		    	  candidate.setIsAdmin(false);
		    	  
		    	  candidate.setCreatedby(pageData.getUser().getCandidateid());
		    	  candidate.setCreatedon(new Timestamp(System.currentTimeMillis()));
		    	  candidate.setModifiedby(pageData.getUser().getCandidateid());
		    	  candidate.setModifiedon(new Timestamp(System.currentTimeMillis()));
		    	  
		    	  securityService.addNewCandidate(candidate, new AsyncCallback<Void>(){
					public void onFailure(Throwable exp) {
						lLabel.setText(caught.getMessage());
						caught = exp;
					}
					public void onSuccess(Void result) {
						lLabel.setText("Added candidate to the system.");
						candidateBox.addItem(idTB.getText());
						dialogBox.hide();
					}});
		      }
		    });
		butPanel.add(addButton);
		// Add a close button at the bottom of the dialog
		Button cancelButton = new Button("Cancel",
		    new ClickHandler() {
		      public void onClick(ClickEvent event) {
		        dialogBox.hide();
		      }
		    });
		butPanel.add(cancelButton);
		
		// Create a table to layout the content
		VerticalPanel dialogContents = new VerticalPanel();
		dialogContents.setSpacing(4);
		
		dialogContents.add(lLabel);
		dialogContents.add(grid);
		dialogContents.add(butPanel);
		
		dialogBox.setWidget(dialogContents);
						
		// Return the dialog box
		return dialogBox;
	}
	
	class NumericKeyHandler implements KeyUpHandler
	{
		TextBox sender;
		NumericKeyHandler(TextBox sender){
			this.sender = sender;
		}
		public void onKeyUp(KeyUpEvent event) {
			char keyCode = (char)event.getNativeKeyCode();
			if ((!Character.isDigit(keyCode)) && (keyCode != (char) KeyCodes.KEY_TAB)
		              && (keyCode != (char) KeyCodes.KEY_BACKSPACE)
		              && (keyCode != (char) KeyCodes.KEY_DELETE) && (keyCode != (char) KeyCodes.KEY_ENTER) 
		              && (keyCode != (char) KeyCodes.KEY_HOME) && (keyCode != (char) KeyCodes.KEY_END)
		              && (keyCode != (char) KeyCodes.KEY_LEFT) && (keyCode != (char) KeyCodes.KEY_UP)
		              && (keyCode != (char) KeyCodes.KEY_RIGHT) && (keyCode != (char) KeyCodes.KEY_DOWN)) {
		            // TextBox.cancelKey() suppresses the current keyboard event.
		            sender.cancelKey();
		          }
		}
	}
}